home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
CU Amiga Super CD-ROM 11
/
CU Amiga Magazine's Super CD-ROM 11 (1997)(EMAP Images)(GB)(Track 1 of 3)[!][issue 1997-06].iso
/
www
/
http
/
www.amigasupport.com
/
software
/
arc
/
fpwav.lha
/
Source
/
dispatch.c
< prev
next >
Wrap
C/C++ Source or Header
|
1995-01-21
|
13KB
|
458 lines
/******************************************************************************
*
* WAV Datatype, based on the sourcecode found in OS3.1 Native Developer Kit
*
* Written by David N.Junod and Christian Buchner
*
******************************************************************************
* dispatch.c
* TO DO: Implement different decompression techniques (ADPCM, µ-law, a-law...)
*/
#include "classbase.h"
/*****************************************************************************/
#define DEBUG 0
#if DEBUG
#define DB(x) x
#include <stdarg.h>
void __stdargs Error(struct ClassBase *cb,UBYTE *Msg,...)
{
va_list Arg;
struct EasyStruct Req={sizeof(struct EasyStruct),0,"WAV debug message",0,"Okay"};
va_start(Arg,Msg);
Req.es_TextFormat=Msg;
EasyRequestArgs(NULL,&Req,0,Arg);
va_end(Arg);
}
#else
#define DB(x)
#endif
/*****************************************************************************/
Class *initClass (struct ClassBase * cb)
{
Class *cl;
if (cl = MakeClass (WAVDTCLASS, SOUNDDTCLASS, NULL, NULL, 0L))
{
cl->cl_Dispatcher.h_Entry = (ULONG (*)())Dispatch;
cl->cl_UserData = (ULONG) cb;
AddClass (cl);
}
return (cl);
}
/*****************************************************************************/
ULONG ASM Dispatch (REG (a0) Class * cl, REG (a2) Object * o, REG (a1) Msg msg)
{
struct ClassBase *cb = (struct ClassBase *) cl->cl_UserData;
ULONG retval = 0L;
switch (msg->MethodID)
{
case OM_NEW:
if (retval = DoSuperMethodA (cl, o, msg))
{
if (!(ConvertObjectData (cb, cl, (Object *) retval, ((struct opSet *) msg)->ops_AttrList)))
{
CoerceMethod (cl, (Object *) retval, OM_DISPOSE);
retval = NULL;
}
}
break;
/* Let the superclass handle everything else */
default:
retval = (ULONG) DoSuperMethodA (cl, o, msg);
break;
}
return (retval);
}
/*****************************************************************************/
#define SWAPW(a) (WORD)(((UWORD)a>>8)+((((UWORD)a&0xff)<<8)))
#define SWAPU(a) (UWORD)(((UWORD)a>>8)+((((UWORD)a&0xff)<<8)))
#define SWAPL(a) (LONG)(((ULONG)a>>24)+(((ULONG)a&0xff0000)>>8)+(((ULONG)a&0xff00)<<8)+(((ULONG)a&0xff)<<24))
/*****************************************************************************/
#define fmt_ID MAKE_ID('f','m','t',' ')
#define data_ID MAKE_ID('d','a','t','a')
/*****************************************************************************/
/* RIFF header */
struct RIFFHeader
{
ULONG rh_RIFF;
ULONG rh_Size;
ULONG rh_Format;
};
/*****************************************************************************/
/* RIFF chunk */
struct RIFFChunk
{
ULONG rc_ID;
ULONG rc_Size;
};
/*****************************************************************************/
struct WaveFormat
{
WORD wf_Format;
WORD wf_Channels;
ULONG wf_SamplesPerSec;
ULONG wf_AvgBytesPerSec;
WORD wf_BlockAlign;
};
/* wf_Format values */
#define WAVE_FORMAT_PCM 1
/*****************************************************************************/
struct PCMData
{
UWORD pd_BitsPerSample;
};
/*****************************************************************************/
/*****************************************************************************/
BOOL ConvertObjectData (struct ClassBase * cb, Class * cl, Object * o, struct TagItem * attrs)
{
LONG ErrorCode=0;
struct FileInfoBlock *fib;
struct VoiceHeader *vhdr;
STRPTR Title;
ULONG Memory;
LONG size;
BPTR FH;
ULONG SampleLength;
UBYTE *Sample;
/* Maximum block size for file IO */
ULONG MAXBUF=16384;
/* IO related */
UBYTE *buffer;
ULONG ID;
ULONG ChunkLen;
ULONG Left;
ULONG Got;
/* RIFF related */
BOOL GotWaveFormat=FALSE;
UWORD Format;
UWORD Channels;
ULONG SamplesPerSec;
UWORD BitsPerSample;
UWORD BytesPerSample;
Title = (UBYTE*)GetTagData(DTA_Name, NULL, attrs);
getdtattrs (cb, o,
SDTA_VoiceHeader, &vhdr,
DTA_Handle, &FH,
TAG_DONE);
if (FH && vhdr)
{
/* Allocate a temporary file info block */
if (!(fib = (struct FileInfoBlock *) AllocMem (sizeof (struct FileInfoBlock), NULL)))
{
ErrorCode=ERROR_NO_FREE_STORE;
DB (Error (cb,"not enough memory"));
}
else
{
/* Get the size of the file */
if (ExamineFH (FH, fib))
{
MAXBUF = size = fib->fib_Size;
}
else
{
Seek (FH, 0, OFFSET_END);
MAXBUF = size = Seek (FH, 0, OFFSET_BEGINNING);
}
/* Free the temporary file info block */
FreeMem (fib, sizeof (struct FileInfoBlock));
/* Limit the size of the IO block to 32K */
if (MAXBUF>32768) MAXBUF=32768;
/* Allocate Buffered IO block */
if (!(buffer = AllocVec(MAXBUF, MEMF_ANY)))
{
ErrorCode=ERROR_NO_FREE_STORE;
DB (Error (cb,"not enough memory"));
}
else
{
/* Read the RIFF header into the buffer */
Get(cb, FH, buffer, sizeof(struct RIFFHeader) ,&ErrorCode);
if (!ErrorCode)
{
/* Check the RIFF header */
if (((struct RIFFHeader*)buffer)->rh_RIFF!=MAKE_ID('R','I','F','F') ||
((struct RIFFHeader*)buffer)->rh_Format!=MAKE_ID('W','A','V','E'))
{
ErrorCode=ERROR_OBJECT_WRONG_TYPE;
DB(Error(cb,"unknown format"));
}
else
{
/* Get size from RIFF header */
ChunkLen = SWAPL (((struct RIFFHeader*)buffer)->rh_Size) + 8;
DB (Error (cb,"RIFF: %ld", ChunkLen));
/* Make sure we have the right size */
/* one missing byte is tolerated! */
if (ChunkLen != size && ChunkLen != size+1)
{
ErrorCode=ERROR_BAD_HUNK;
DB (Error (cb,"mangled file"));
}
else
{
/* Loop: Read in chunk header till EOF (Got=0) */
while((!ErrorCode) && (Got=Get(cb, FH, buffer,sizeof(struct RIFFChunk),&ErrorCode)))
{
if (!ErrorCode)
{
/* Get chunk ID and len */
ID = ((struct RIFFChunk*)buffer)->rc_ID;
Left = ChunkLen = SWAPL(((struct RIFFChunk*)buffer)->rc_Size);
DB (Error (cb,"Chunk: '%lc%lc%lc%lc', %ld", (ULONG)(ID>>24), (ULONG)(ID>>16)&0xff, (ULONG)(ID>>8)&0xff, (ULONG)(ID&0xff), ChunkLen));
/* Read as much of the chunk as fits into buffer */
Left-=(Got=Get(cb, FH, buffer, Left > MAXBUF ? MAXBUF : Left, &ErrorCode));
if (!ErrorCode)
{
/* Handle the chunk types */
switch (ID)
{
/* WaveForm structure */
case fmt_ID:
{
if (Left!=0)
{
/* format chunk CANNOT be larger than buffer!!! */
ErrorCode=ERROR_BAD_HUNK;
DB(Error(cb,"format chunk overflow"));
}
else
{
struct WaveFormat *wf=(struct WaveFormat *)buffer;
Format = SWAPW (wf->wf_Format);
Channels = SWAPW (wf->wf_Channels);
SamplesPerSec = SWAPL (wf->wf_SamplesPerSec);
DB (Error (cb,"Wave Format\n"
"Format: %ld\n"
"Channels: %ld\n"
"SPS: %ld\n"
"Avg.BPS: %ld\n"
"Align: %ld",
(LONG) Format,
(LONG) Channels,
(LONG) SamplesPerSec,
(LONG) SWAPL (wf->wf_AvgBytesPerSec) ,
(LONG) SWAPW (wf->wf_BlockAlign)));
/* We can only handle PCM format */
if (Format!=WAVE_FORMAT_PCM)
{
ErrorCode=DTERROR_UNKNOWN_COMPRESSION;
DB (Error (cb,"unknown compression"));
}
else
{
BitsPerSample = SWAPW(((struct PCMData*)(buffer+sizeof(struct WaveFormat)))->pd_BitsPerSample);
BytesPerSample = BitsPerSample/8;
DB (Error (cb,"BitsPerSample: %ld",(LONG)BitsPerSample));
/* Make sure we have something that we can really handle */
if ( (BitsPerSample!=8 && BitsPerSample!=16) || (Channels!=1 && Channels!=2) )
{
ErrorCode=ERROR_OBJECT_WRONG_TYPE;
DB (Error (cb,"unsupported type"));
}
else
{
GotWaveFormat=TRUE;
}
}
}
}
break;
/* Sound data */
case data_ID:
{
/* Make sure we have had a WaveFormat structure */
if (!GotWaveFormat)
{
ErrorCode=ERROR_REQUIRED_ARG_MISSING;
DB (Error (cb,"no waveform structure"));
}
else
{
/* sound.datatype V40 can replay */
/* directly from Fast RAM */
Memory = (SuperClassBase->lib_Version>39) ?
MEMF_ANY : MEMF_CHIP;
SampleLength = (ChunkLen/Channels)/BytesPerSample;
if (!(Sample = AllocVec (SampleLength, Memory)))
{
ErrorCode=ERROR_NO_FREE_STORE;
DB (Error (cb,"not enough memory"));
}
else
{
/* Sample conversion / buffered loading loop */
ULONG Offset=0;
while (!ErrorCode)
{
/* Convert the data to 8 bit signed format */
/* Efficiently mix all channels into one */
/* and copy the Sample to chip memory */
ULONG DestBlockLen=(Got/Channels)/BytesPerSample;
UBYTE *dst = (Memory == MEMF_CHIP) ? buffer : Sample+Offset;
if (BitsPerSample==8 && Channels==1)
ConvertMono8(buffer,dst,DestBlockLen);
if (BitsPerSample==8 && Channels==2)
ConvertStereo8(buffer,dst,DestBlockLen);
if (BitsPerSample==16 && Channels==1)
ConvertMono16(buffer,dst,DestBlockLen);
if (BitsPerSample==16 && Channels==2)
ConvertStereo16(buffer,dst,DestBlockLen);
/* For V39 soundclass only: */
/* Now copy sound data into chip RAM.*/
/* This is significantly faster than */
/* decoding directly into Chip RAM. */
if (Memory == MEMF_CHIP)
CopyMem( buffer, Sample+Offset, DestBlockLen );
/* Increase offset in destination data */
Offset+=DestBlockLen;
/* Read on if data left otherwise exit loop */
if (Left)
Left-=(Got=Get(cb, FH, buffer, Left > MAXBUF ? MAXBUF : Left, &ErrorCode));
else
break;
}
/* Fill in the VoiceHeader */
memset(vhdr,0,sizeof(struct VoiceHeader));
vhdr->vh_OneShotHiSamples = SampleLength;
vhdr->vh_SamplesPerSec = SamplesPerSec;
vhdr->vh_Octaves = 1;
vhdr->vh_Compression = CMP_NONE;
vhdr->vh_Volume = 64;
/* Tell the super-class about the attributes */
setdtattrs (cb, o,
DTA_ObjName, Title,
SDTA_Sample, Sample,
SDTA_SampleLength, SampleLength,
SDTA_Period, (ULONG)(SysBase->ex_EClockFrequency*5)/(ULONG)vhdr->vh_SamplesPerSec,
SDTA_Volume, 64,
SDTA_Cycles, 1,
TAG_DONE);
}
}
}
break;
}
}
}
}
}
}
}
/* Free the buffer */
FreeVec (buffer);
}
}
}
if (ErrorCode)
{
SetIoErr(ErrorCode);
return(FALSE);
}
return(TRUE);
}
/*****************************************************************************/
ULONG Get(struct ClassBase *cb, BPTR FH, UBYTE *buf, ULONG size, LONG *ErrorCode)
{
LONG got;
/* read in size bytes */
got=Read(FH, buf, size);
/* Io error? */
if (got<0)
{
*ErrorCode=IoErr();
got=0;
}
/* don't care at all about short reads */
return((ULONG)got);
}
/*****************************************************************************/
ULONG setdtattrs (struct ClassBase * cb, Object * o, ULONG data,...)
{
return (SetDTAttrsA (o, NULL, NULL, (struct TagItem *) & data));
}
/*****************************************************************************/
ULONG getdtattrs (struct ClassBase * cb, Object * o, ULONG data,...)
{
return (GetDTAttrsA (o, (struct TagItem *) & data));
}